package com.ws.demo1.demos;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ws.demo1.demos.domain.*;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 一个实用的“缠论”入门级实现：
 * - 顶/底分型
 * - 笔（连接交替分型）
 * - 线段（基于三笔成段的启发式）
 * - 中枢（至少三笔的价格重叠区间，可向后扩展）
 * 注意：缠论的严格规则较复杂，下面实现偏工程启发式，便于画图与直观观察。
 */
public class ChanAnalyzer {

    // 1日线，2 其他
    public static int lineNumber = 400;
    public static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    // 获取原始K线数据并合并
    public static List<MarketData> fetchOriginalData() {
        RestTemplate restTemplate = new RestTemplate();
        String result = restTemplate.getForObject(
                "https://stock2.finance.sina.com.cn/futures/api/jsonp.php/var%20_FG2601_30_1760010687218=/InnerFuturesNewService.getFewMinLine?symbol=FG2601&type=5",
                String.class
        );

        // 提取JSON数组部分
        String jsonData = result.substring(result.indexOf("["), result.lastIndexOf("]") + 1);
        JSONArray jsonArray = JSONArray.parseArray(jsonData);

        List<MarketData> list = new ArrayList<>();
        // 转换为K线数据对象
        for (Object o : jsonArray) {
            JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(o));
            MarketData marketData = new MarketData();

            marketData.setDt(LocalDateTime.parse(jsonObject.getString("d"), dateTimeFormatter));
            marketData.setHigh(jsonObject.getDoubleValue("h"));
            marketData.setLow(jsonObject.getDoubleValue("l"));
            list.add(marketData);
        }

        if (list.size() > lineNumber) {
            list = list.subList(list.size() - lineNumber, list.size());
        }

        list = mergeKLines(list);

        return list;
    }

    // 合并K线
    public static List<MarketData> mergeKLines(List<MarketData> dataList) {
        List<MarketData> merged = new ArrayList<>();
        merged.add(dataList.get(0));
        merged.add(dataList.get(1));
        for (int i = 2; i < dataList.size(); i++) {
            MarketData curr = dataList.get(i);
            MarketData last = merged.get(merged.size() - 1);
            MarketData lastLast = merged.get(merged.size() - 2);
            // 处理包含关系
            boolean b1 = curr.getHigh() <= last.getHigh() && curr.getLow() >= last.getLow();
            boolean b2 = curr.getHigh() >= last.getHigh() && curr.getLow() <= last.getLow();
            if (b1 && b2) {
                // 判断是上升还是下跌
                boolean up = last.getHigh() > lastLast.getHigh();
                if (up) {
                    // 上升趋势，高高
                    last.setHigh(Math.max(last.getHigh(), curr.getHigh()));
                    last.setLow(Math.max(last.getLow(), curr.getLow()));
                    last.setDt(curr.getDt());
                } else {
                    // 下降趋势，低低
                    last.setHigh(Math.min(last.getHigh(), curr.getHigh()));
                    last.setLow(Math.min(last.getLow(), curr.getLow()));
                    last.setDt(curr.getDt());
                }
            } else {
                merged.add(curr);
            }
        }
        return merged;
    }

    // 找出分型
    public static List<Fractal> detectFractals(List<MarketData> dataList) {
        List<Fractal> result = new ArrayList<>();
        for (int i = 1; i < dataList.size() - 1; i++) {
            double h0 = dataList.get(i - 1).getHigh();
            double h1 = dataList.get(i).getHigh();
            double h2 = dataList.get(i + 1).getHigh();

            double l0 = dataList.get(i - 1).getLow();
            double l1 = dataList.get(i).getLow();
            double l2 = dataList.get(i + 1).getLow();

            if (h1 > h0 && h1 > h2) {
                result.add(new Fractal(i, dataList.get(i).getDt(), h1, FractalType.TOP));
            } else if (l1 < l0 && l1 < l2) {
                result.add(new Fractal(i, dataList.get(i).getDt(), l1, FractalType.BOTTOM));
            }
        }
        // 过滤连续同类型分型：保留更极端者，使类型交替
        result = filterAlternateFractals(result);
        return result;
    }

    // 过滤连续同类型分型：保留更极端者，使类型交替
    public static List<Fractal> filterAlternateFractals(List<Fractal> fractals) {
        List<Fractal> filtered = new ArrayList<>();
        for (Fractal f : fractals) {
            if (filtered.isEmpty()) {
                filtered.add(f);
                continue;
            }
            Fractal last = filtered.get(filtered.size() - 1);
            if (last.type == f.type) {
                // 同类型，保留更极端
                if (f.type == FractalType.TOP) {
                    if (f.price >= last.price) {
                        filtered.set(filtered.size() - 1, f);
                    }
                } else {
                    if (f.price <= last.price) {
                        filtered.set(filtered.size() - 1, f);
                    }
                }
            } else {
                filtered.add(f);
            }
        }
        return filtered;
    }

    // 从分型产出笔（连接交替分型，需满足最小振幅要求）
    public static List<Pen> buildPens(List<Fractal> fractals, double minDeltaAbs, double minDeltaPct) {
        List<Pen> pens = new ArrayList<>();
        for (int i = 1; i < fractals.size(); i++) {
            Fractal a = fractals.get(i - 1);
            Fractal b = fractals.get(i);
            // 理论上已过滤
            if (a.type == b.type) {
                continue;
            }
            // 确保两个分型之间有至少一根 K 线
            if (b.index - a.index <= 1) {
                continue;
            }
            double delta = Math.abs(b.price - a.price);
            double pct = delta / Math.max(1e-9, a.price);
            if (delta >= minDeltaAbs || pct >= minDeltaPct) {
                pens.add(new Pen(a, b));
            }
        }
        return pens;
    }

    // 中枢结果与线段结果
    public static class ZhongShuAndSegments {
        public final List<ZhongShu> zhongShus;
        public final List<Segment> segments;

        public ZhongShuAndSegments(List<ZhongShu> zhongShus, List<Segment> segments) {
            this.zhongShus = zhongShus;
            this.segments = segments;
        }
    }

    // 探测中枢（至少三笔的价格重叠），并产出对应的线段（从首笔起点到末笔终点）
    public static ZhongShuAndSegments detectZhongShuAndSegments(List<Pen> pens) {
        List<ZhongShu> zsList = new ArrayList<>();
        List<Segment> segs = new ArrayList<>();

        int i = 0;
        while (i <= pens.size() - 3) {
            Range overlap = overlap3(rangeOf(pens.get(i)), rangeOf(pens.get(i + 1)), rangeOf(pens.get(i + 2)));
            if (overlap == null) {
                i++;
                continue;
            }
            // 有三笔重叠，向后扩展
            int j = i + 3;
            Range zone = new Range(overlap.low, overlap.high);
            while (j < pens.size()) {
                Range next = rangeOf(pens.get(j));
                Range inter = overlap2(zone, next);
                if (inter == null) break;
                zone = inter;
                j++;
            }
            // 记录中枢（对应 K 线索引范围用笔的起止 fractal）
            Pen first = pens.get(i);
            Pen last = pens.get(j - 1);
            ZhongShu zs = new ZhongShu(first.start.index, last.end.index, zone.low, zone.high);
            zsList.add(zs);

            // 简化生成线段：以首笔起点到末笔终点，方向由端点价格决定
            boolean up = last.end.price > first.start.price;
            segs.add(new Segment(first.start.index, last.end.index, first.start.price, last.end.price, up));

            // 下一个中枢起点滑动（可重叠或跳过重叠区）
            i = j - 1; // 留一定重叠
        }

        return new ZhongShuAndSegments(zsList, segs);
    }

    // 价格区间
    public static Range rangeOf(Pen p) { return new Range(p.low(), p.high()); }

    // 价格区间
    public static Range overlap2(Range a, Range b) {
        double low = Math.max(a.low, b.low);
        double high = Math.min(a.high, b.high);
        if (low <= high) return new Range(low, high);
        return null;
    }

    // 三个价格区间的重叠
    public static Range overlap3(Range a, Range b, Range c) {
        Range ab = overlap2(a, b);
        if (ab == null) return null;
        return overlap2(ab, c);
    }
}
